Next JS - Authentication Kullanıcı Ekleme

Next JS - Authentication Kullanıcı Ekleme
23 Ağustos 2024
#Next.Js#React#Yazılım#Authentication#Next-Auth

NextAuth.js'in Next.js uygulamaları için tasarlanmış, güçlü bir kimlik doğrulama kütüphanesidir. Kullanımı oldukça basit olmakla birlikte bir çok şekilde özelleştirilebilmektedir. Google, Github gibi OAuth sağlayıcılarını kullanabileceğiniz gibi user bilgilerini kendi veritabanınızda da tutabilirsiniz. NextAuth.js JWT tabanlı olduğu için API güvenliğini de sağlar. Biz bu yazıda user bilgilerini kendi veritabanımız da (mongo db) tutacağımız bir senaryoyu anlatacağız. Şimdi adım adım uygulamamıza NextAuth.js adaptasyonunu gerçekleştirelim. 

1. NextAuth.js Kurulumu

Öncelikle next-auth paketini uygulamaya yükleyelim ; npm install next-auth

Daha sonra .env dosyamıza rastgele bir şifre değeri oluşturalım ; NEXTAUTH_SECRET = sdlasdgkuhohpıojjaosd52346

Bu işlemlerde sonra bize gereken dosyaları oluşturmaya başlayalım.

2. User Model Oluşturma

User verilerini saklamak için bu örnekte mongo-db veri tabanı kullanıldı. Siz farklı veri tabanı kullanıyorsanız, bu kodu ona göre değiştirebilirsiniz. 

import mongoose, { Document, Schema } from"mongoose";


interface IUser extends Document {
  isim: string;
  email: string;
  password: string;
  createdAt?: Date;
  updatedAt?: Date;
}

const UserSchema: Schema = newSchema(
  {
    isim: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
    },
    password: {
      type: String,
      required: true,
    },
  },
  { timestamps:true }
);

const UserModel=
  mongoose.models.User || mongoose.model <IUser> ("User", UserSchema);

exportdefaultUserModel;

3. Login Page Fonksiyonu

Login Page'de kullanıcıdan aldığınız veriyi aşağıda gösterildiği gibi next-auth eklentisinin "signIn fonksiyonuna göndermeniz gerekiyor. Router replace ile login ve auth işlemi başarılı olursa yönlendirilecek adresi yazıyorsunuz.

"use client";
import { signIn } from "next-auth/react";
import { useRouter } from "next/navigation";


import { toast } from "react-toastify";



const LoginForm:React.FC= () => {
const router = useRouter();
  const [loading, setLoading] =useState(false);

  const submitLogin = async (event:React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setLoading(true);
    const formData = new FormData(event.currentTarget);

    try {
      const response = await signIn("credentials", {
        email:formData.get("email") as string,
        password:formData.get("password") as string,
        redirect:false,
      });

      if (response?.error) {
        toast.error("Böyle bir kullanıcı bulunmamaktadır");
        return;
      }

      router.replace("/");
    } catch (error) {
      toast.error(
        "Bir hata oluştu: "+
          (errorinstanceofError?error.message:String(error))
      );
    } finally {
      setLoading(false);
    }
  };
  return ( <LoginPage/>)
};

export default LoginForm;

4. Register Page Fonksiyonu,

Register sayfasından aldığınız veritabanına passwordu şifreleyerek gönderebilirsiniz.  Aşağıda örneği gösterilmiştir. Register sayfasının next-auth ile pek bir ilişkisi bulunmuyor.

export const addUser = async (
  prevState:any,
  formData:any
):Promise<InsertResponse> => {
  try {
    const isim = formData.get("isim")?.toString();
    const email = formData.get("email")?.toString();
    const password = formData.get("password")?.toString();
    if (!isim||!email||!password) {
      return { msg:"Tüm alanları doldurun", status:false };
    }
    await dbConnect();
    const existingUser = await UserModel.findOne({ email }).select("_id");
    if (existingUser) {
      return { msg:"Bu kullanıcı zaten kayıtlıdır", status:false };
    }
    const hashedPassword = await bcrypt.hash(password, 10);
    const userData = { isim, email, password:hashedPassword };
    await UserModel.create(userData);
    return { msg:"Kullanıcı başarıyla kaydedildi", status:true };
  } catch (error) {
    console.error(`Kullanıcı eklenemedi: ${error}`);
    return {
      msg:`Kullanıcı eklenemedi: ${
        errorinstanceofError ? error.message : "Bilinmeyen hata"
      }`,
      status:false,
    };
  }
};

5. Auth için API Route oluşturma

NextJs dosya yapısı içerisinde app klasörü => api => [...nextauth] klasörü oluşturun.

Onun içerisinde de route ve option isimli iki js veya ts dosyası oluşturun.

auth-pic-3

Bu dosyalardan route.ts aşağıdaki gibi olsun;

import NextAuth from "next-auth/next";
import { NextAuthOptions } from "next-auth";
import { options } from "./options";

const handler = NextAuth( options as NextAuthOptions);

export { handler as GET, handler as POST };

Sonrasında bu fonksiyon için yazacağımız option dosyası da aşağıdaki şekilde olmalıdır. Bu option kısmı esas işi gören kısım olacaktır.

import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import UserModel from "@/lib/models/UserModel";
import dbConnect from "@/lib/config/dbConnect";
import { NextAuthOptions } from "next-auth";

export const options : NextAuthOptions= {
  providers: [
    CredentialsProvider({
      name:"Email",
      credentials: {
        email: { label:"Email", type:"email" },
        password: { label:"Şifre", type:"password" },
      },
      async authorize(credentials) {
        const { email, password } = credentials || {};

        if (!email || !password) {
          returnnull;
        }

        try {
          await dbConnect();
          const user = await UserModel.findOne({ email });

          if (!user) {
            return null;
          }

          const passwordsMatch = await bcrypt.compare(password, user.password);

          if (!passwordsMatch) {
            return null;
          }

          return user;
        } catch (error) {
          console.error("Error during authorization:", error);
          return null;
        }
      },
    }),
  ],
  session: {
    strategy:"jwt",
  },
  secret:process.env.NEXTAUTH_SECRET,
  pages: {
    signIn:"/",
    signOut:"/login",
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
      }
      return token;
    },
    async session({ session, token }) {
      if (token) {
        session.user.id = token.id;
      }
      return session;
    },
  },
  jwt: {
    maxAge : 60*60*24*30,
  },
};

Burada ki ayarlar tamamen size özel özelleştirilebilir.

6. Auth Provider Oluşturma

Uygulamamız da oluşturduğumuz auth özellikleirni kullanabilmek için provider oluşturmamız ve bu provider'ı, eğer auth tüm app boyutunda ise ana layout'a, değilse auth olacak sayfaların en üst layout'ına yazmamız gerekiyor. Öncelikle App klasörüne "provider" isimli bir js veya ts dosyası oluşturun. İçeriğini şu şekilde doldurun.

"use client";
import { SessionProvider } from "next-auth/react";
import { ReactNode } from "react";

interface AuthProviderProps {
  children: ReactNode;
}

export const AuthProvider:React.FC<AuthProviderProps> = ({ children }) => {
  return(
<SessionProvider>
{children}</SessionProvider>)
};

Oluşturduğumuz bu provider'ı ana sayfa ve tüm app boyunca kullanmak istiyorum bu durumda app klasörü içerisinde ki layout kalsörüneün en üstüne ekliyorum.

export default function RootLayout({ children }: RootLayoutProps) { return ( <html lang="en"> <body className={myFont.className}>
<AuthProvider> {children}
</AuthProvider>
</body> </html> ); }

7. Auth Middleware Oluşturma

Auth gereken sayfaları belirtmenin bir çok yolu var aslında. Sayfaların kendinden de yapabilirim. Ancak bu işin kolay yolu bir adet middleware yazarak tüm şifre istenecek yolları girmek.

Bu nedenle proje kök dizinine (app dosyası için değil, app dosyasıyla aynı seviyeye) middleware isminde bir js veya ts dosyası oluşturun.

export { default } from "next-auth/middleware";


export const config= {
  matcher: ["/", "/calculation", "/register", "/parameters", "/statistics"],
};

Bu dosyanın matcher kısmına user yetkisi ile girilecek uzantıları yazıyorsunuz. Bu aşamadan sonra bu uzantılara girmek için login olmak zorunlu olmuş oluyor. Bu uzantıların alt uzantıları da (örneğin "/parameters/orders") yine auth zorunluluğuna girmiş oluyor.

8. Auth Tip Ayarlarını Oluşturma

Bu kısım typesctipt kullananlar için uygulanması gereken bir durum. Normal javascript ile yazıyorsanız, bu dosyayı oluşturmanız gerekmiyor. Typescript kullananların "next-auth.d.ts" isimli bir klasör oluşturması gerekiyor. Yeri çok önemli değil ama diğer tip dosylarınızın yanında oluşturmanız iyi olacaktır. İçeriği aşağıdaki şekilde oluşturabilirsiniz.

import NextAuth from "next-auth";
import { DefaultSession } from"next-auth";

declare module "next-auth" {
  interface User {
    id: string;
  }
  interface Session {
    user: {
      id: string | unknown;
    } & DefaultSession["user"];
  }
  interface JWT {
    id: string;
  }
}

Böylece işlemleri tamamlamış olduk. Bunların dışında sizin tek yapmanız gereken register ve login page yazarak kullanıcıdan verileri almak. Auth kısmını burada belirtilen özellikler ile yapabilirsiniz. Yine bir sorunuz olursa iletişiem geçebilirsiniz.


Benzer Yazılar

Bekleyiniz ...

Bekleyiniz ...

footer_logo

Bütün Hakları Saklıdır @2024

mehmetaltann@gmail.com